package edu.nazarevych.portal.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.hibernate.criterion.Criterion;

import edu.nazarevych.portal.entities.BaseEntity;

public class BaseDao<T extends BaseEntity> {

	protected final Class<T> persistentClass;
	protected Session session;
	protected Transaction transaction;

	public BaseDao(final Class<T> persistentClass) {
		this.persistentClass = persistentClass;
	}

	protected void commitTransactionAndCloseSession() {
		transaction.commit();
		session.close();
	}

	protected void openSessionAndBeginTransaction() {
		session = HibernateSessionFactory.sessionFactory.openSession();
		transaction = session.beginTransaction();
	}

	protected void openSession() {
		session = HibernateSessionFactory.sessionFactory.openSession();
	}

	protected void closeSession() {
		if (session != null) {
			session.close();
		}
	}

	public List<T> findAll() {
		return findByCriterions();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByCriterions(final Criterion... criterions) {
		openSessionAndBeginTransaction();
		Criteria criteria = session.createCriteria(persistentClass);
		for (Criterion criterion : criterions) {
			if (criterion != null) {
				criteria.add(criterion);
			}
		}
		List<T> entities = criteria.list();
		commitTransactionAndCloseSession();

		return entities;
	}

	@SuppressWarnings("unchecked")
	public T findByID(final Long id) {
		if (id == null) {
			return null;
		}
		T entity = null;
		openSession();
		entity = (T) session.get(persistentClass, id);
		closeSession();
		return entity;
	}

	public T insertOrUpdate(final T entity) {
		if (entity.getId() == null) {
			return insert(entity);
		} else {
			return update(entity);
		}
	}

	public void delete(final T entity) {
		openSessionAndBeginTransaction();
		session.delete(entity);
		commitTransactionAndCloseSession();
	}

	private T insert(final T entity) {
		openSessionAndBeginTransaction();
		Long id = (Long) session.save(entity);
		entity.setId(id);
		commitTransactionAndCloseSession();

	return entity;
	}

	private T update(final T entity) {
		openSessionAndBeginTransaction();
		session.update(entity);
		commitTransactionAndCloseSession();

		return entity;
	}

	@Override
	protected void finalize() throws Throwable {
		closeSession();
		super.finalize();
	}
}
